"""
设备驱动基类
定义所有设备驱动的统一接口
"""

from abc import ABC, abstractmethod
import logging
import time
from datetime import datetime
from typing import Dict, List, Any, Optional
from dataclasses import dataclass
from enum import Enum

class ConnectionStatus(Enum):
    """连接状态枚举"""
    DISCONNECTED = "disconnected"
    CONNECTING = "connecting"
    CONNECTED = "connected"
    ERROR = "error"

class DataType(Enum):
    """数据类型枚举"""
    BOOL = "bool"
    INT16 = "int16"
    INT32 = "int32"
    FLOAT = "float"
    DOUBLE = "double"
    STRING = "string"

@dataclass
class DataPoint:
    """数据点定义"""
    name: str
    description: str
    address: str
    data_type: DataType
    access: str = "R"  # R, W, RW
    unit: str = ""
    scaling: Optional[Dict[str, float]] = None
    alarm: Optional[Dict[str, Any]] = None
    
    def apply_scaling(self, raw_value: float) -> float:
        """应用量程转换"""
        if not self.scaling:
            return raw_value
            
        raw_low = self.scaling.get('raw_low', 0)
        raw_high = self.scaling.get('raw_high', 1)
        eng_low = self.scaling.get('eng_low', 0)
        eng_high = self.scaling.get('eng_high', 1)
        
        # 线性量程转换
        ratio = (raw_value - raw_low) / (raw_high - raw_low)
        return eng_low + ratio * (eng_high - eng_low)

@dataclass
class TagValue:
    """标签值数据结构"""
    tag_name: str
    value: Any
    timestamp: datetime
    quality: str = "GOOD"  # GOOD, BAD, UNCERTAIN
    device_name: str = ""
    
    def to_dict(self) -> Dict[str, Any]:
        """转换为字典格式"""
        return {
            'tag_name': self.tag_name,
            'value': self.value,
            'timestamp': self.timestamp.isoformat(),
            'quality': self.quality,
            'device_name': self.device_name
        }

class BaseDriver(ABC):
    """设备驱动基类"""
    
    def __init__(self, config: Dict[str, Any]):
        self.config = config
        self.device_name = config.get('name', 'Unknown')
        self.description = config.get('description', '')
        self.enabled = config.get('enabled', True)
        self.polling_interval = config.get('polling_interval', 1000) / 1000.0  # 转换为秒
        
        self.connection_status = ConnectionStatus.DISCONNECTED
        self.last_error = None
        self.last_connection_time = None
        self.data_points: Dict[str, DataPoint] = {}
        
        # 最新数据缓存
        self.latest_data: Dict[str, TagValue] = {}
        
        # 初始化日志
        self.logger = logging.getLogger(f"{self.__class__.__name__}[{self.device_name}]")
        
        # 解析数据点配置
        self._parse_data_points()
        
        # 连接重试配置
        self.max_retries = config.get('config', {}).get('retries', 3)
        self.retry_count = 0
        self.last_retry_time = None
        
    def _parse_data_points(self):
        """解析数据点配置"""
        points_config = self.config.get('points', [])
        for point_config in points_config:
            try:
                # 兼容 address 和 topic 字段（主要针对 MQTT）
                address = point_config.get('address') or point_config.get('topic') or point_config.get('node_id', '')
                
                data_point = DataPoint(
                    name=point_config['name'],
                    description=point_config.get('description', ''),
                    address=address,
                    data_type=DataType(point_config.get('data_type', 'float')),
                    access=point_config.get('access', 'R'),
                    unit=point_config.get('unit', ''),
                    scaling=point_config.get('scaling'),
                    alarm=point_config.get('alarm')
                )
                self.data_points[data_point.name] = data_point
                self.logger.debug(f"数据点 {data_point.name} 配置成功")
            except Exception as e:
                self.logger.error(f"数据点 {point_config.get('name', 'Unknown')} 配置失败: {e}")
    
    @abstractmethod
    def connect(self) -> bool:
        """连接设备"""
        pass
    
    @abstractmethod
    def disconnect(self):
        """断开连接"""
        pass
    
    @abstractmethod
    def read_tags(self, tag_names: List[str]) -> List[TagValue]:
        """批量读取标签值"""
        pass
    
    @abstractmethod
    def write_tag(self, tag_name: str, value: Any) -> bool:
        """写入标签值"""
        pass
    
    def read_all_tags(self) -> List[TagValue]:
        """读取所有配置的标签"""
        if not self.is_connected():
            return []
            
        tag_names = list(self.data_points.keys())
        tag_values = self.read_tags(tag_names)
        self.latest_data.clear()
        for tag_value in tag_values:
            self.latest_data[tag_value.tag_name] = tag_value
        return tag_values
    
    def is_connected(self) -> bool:
        """检查连接状态"""
        return self.connection_status == ConnectionStatus.CONNECTED
    
    def health_check(self) -> Dict[str, Any]:
        """设备健康检查"""
        return {
            'device_name': self.device_name,
            'connection_status': self.connection_status.value,
            'last_error': self.last_error,
            'last_connection_time': self.last_connection_time.isoformat() if self.last_connection_time else None,
            'retry_count': self.retry_count,
            'enabled': self.enabled,
            'data_points_count': len(self.data_points)
        }
    
    def get_device_info(self) -> Dict[str, Any]:
        """获取设备信息"""
        return {
            'name': self.device_name,
            'description': self.description,
            'type': self.__class__.__name__.replace('Driver', '').lower(),
            'enabled': self.enabled,
            'polling_interval': self.polling_interval,
            'connection_status': self.connection_status.value,
            'data_points': [
                {
                    'name': dp.name,
                    'description': dp.description,
                    'address': dp.address,
                    'data_type': dp.data_type.value,
                    'access': dp.access,
                    'unit': dp.unit
                }
                for dp in self.data_points.values()
            ]
        }
    
    def auto_reconnect(self) -> bool:
        """自动重连逻辑"""
        if self.is_connected():
            return True
            
        current_time = time.time()
        
        # 检查重试间隔
        if self.last_retry_time and current_time - self.last_retry_time < 10:
            return False
            
        # 检查重试次数
        if self.retry_count >= self.max_retries:
            if self.last_retry_time and current_time - self.last_retry_time > 300:  # 5分钟后重置
                self.retry_count = 0
            else:
                return False
        
        self.logger.info(f"尝试重新连接设备 ({self.retry_count + 1}/{self.max_retries})")
        
        try:
            self.connection_status = ConnectionStatus.CONNECTING
            success = self.connect()
            
            if success:
                self.retry_count = 0
                self.last_connection_time = datetime.now()
                self.connection_status = ConnectionStatus.CONNECTED
                self.logger.info("设备重连成功")
                return True
            else:
                self.retry_count += 1
                self.last_retry_time = current_time
                self.connection_status = ConnectionStatus.ERROR
                self.logger.warning("设备重连失败")
                return False
                
        except Exception as e:
            self.retry_count += 1
            self.last_retry_time = current_time
            self.last_error = str(e)
            self.connection_status = ConnectionStatus.ERROR
            self.logger.error(f"设备重连异常: {e}")
            return False
    
    def validate_tag_value(self, tag_name: str, value: Any) -> bool:
        """验证标签值的有效性"""
        if tag_name not in self.data_points:
            return False
            
        data_point = self.data_points[tag_name]
        
        try:
            # 类型验证
            if data_point.data_type == DataType.BOOL:
                return isinstance(value, (bool, int)) and value in [0, 1, True, False]
            elif data_point.data_type in [DataType.INT16, DataType.INT32]:
                return isinstance(value, (int, float)) and int(value) == value
            elif data_point.data_type in [DataType.FLOAT, DataType.DOUBLE]:
                return isinstance(value, (int, float))
            elif data_point.data_type == DataType.STRING:
                return isinstance(value, str)
            else:
                return True
                
        except Exception:
            return False
    
    def process_tag_value(self, tag_name: str, raw_value: Any) -> TagValue:
        """处理标签值（量程转换、数据验证等）"""
        data_point = self.data_points.get(tag_name)
        if not data_point:
            return TagValue(
                tag_name=tag_name,
                value=raw_value,
                timestamp=datetime.now(),
                quality="BAD",
                device_name=self.device_name
            )
        
        try:
            # 数据类型转换
            if data_point.data_type == DataType.BOOL:
                value = bool(raw_value)
            elif data_point.data_type == DataType.INT16:
                value = int(raw_value)
            elif data_point.data_type == DataType.INT32:
                value = int(raw_value)
            elif data_point.data_type == DataType.FLOAT:
                value = float(raw_value)
                # 应用量程转换
                if data_point.scaling:
                    value = data_point.apply_scaling(value)
            elif data_point.data_type == DataType.DOUBLE:
                value = float(raw_value)
                if data_point.scaling:
                    value = data_point.apply_scaling(value)
            elif data_point.data_type == DataType.STRING:
                value = str(raw_value)
            else:
                value = raw_value
            
            return TagValue(
                tag_name=tag_name,
                value=value,
                timestamp=datetime.now(),
                quality="GOOD" if self.validate_tag_value(tag_name, value) else "UNCERTAIN",
                device_name=self.device_name
            )
            
        except Exception as e:
            self.logger.error(f"处理标签 {tag_name} 值失败: {e}")
            return TagValue(
                tag_name=tag_name,
                value=raw_value,
                timestamp=datetime.now(),
                quality="BAD",
                device_name=self.device_name
            ) 